/*
 *      Copyright (C) 2005-2011 Team XBMC
 *      http://www.xbmc.org
 *
 *  This Program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  This Program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with XBMC; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *  http://www.gnu.org/copyleft/gpl.html
 *
 */

#include <stdint.h>
#include <math.h>
#include <memory.h>
#include <sys/types.h>

#include "PVREtp.h"
#include "PVREtpProtocol.h"
#include "platform/threads/threads.h"
#include "avcodec.h"

#include "lib/protocol.h"
#include "lib/device.h"

using namespace std;
using namespace ADDON;
using namespace PLATFORM;

#define ETP_UPDATE_INFO_INTERVAL			5
#define	ETP_NET_BUFFER_COUNT				80
#define ETP_TS_BUFFER_COUNT					172
#define ETP_MEMLIST_COUNT					1000
#define ETP_MEMLIST_PRE_ALLOC				100

#define MAX_STR_SZ							4096
#define VIDEO_FRAME_SIZE_INIT				600000
#define AUDIO_FRAME_SIZE_INIT				5000

#define PVR_URL_HEADER 						"pvr://recordings/"

PVREtp::PVREtp(CStdString host, int port)
{
	int i;
	m_demuxPacketBuffer = new PLATFORM::SyncedBuffer<DemuxPacket*>(ETP_NET_BUFFER_COUNT);
	m_NetworkBuffer = new PLATFORM::SyncedBuffer<uint8_t*>(ETP_NET_BUFFER_COUNT);
	m_TimerCache = new PLATFORM::SyncedBuffer<PVR_TIMER*>(200);
	m_RecCache = new PLATFORM::SyncedBuffer<PVR_RECORDING*>(500);

	m_ForceUpdateInfo = false;
	m_EtpStreamCount = 0;
	m_PidMapCount = 0;
	m_UseMemList = 1;
	m_StreamIsRecording = false;
	m_RecSeqNum = -1;
	m_TimerSeqNum = -1;
	m_TimerCount = 0;
	m_StartTime = 0;
	m_PacketCount = 0;
	m_strRemoteIP = host;
	m_iRemotePort = port;
	m_usRemoteDataPort = 0;
	m_TsBufferSize = TS_PACKET_SIZE * ETP_TS_BUFFER_COUNT;


	m_APushed = 0;
	m_VPushed = 0;

	for(i=0; i < PVR_STREAM_MAX_STREAMS; i++)
	{
		memset(&m_EtpStream[i], 0, sizeof(ETP_STREAM_PACKET));
		memset(&m_PidMap[i], 0, sizeof(ETP_PID_MAP));
	}

	m_ETPProto = new PVREtpProtocol(m_strRemoteIP, m_iRemotePort);

	memset(&m_ReadThreadOpts, 0, sizeof(THREAD_OPTS));
	memset(&m_FramesThreadOpts, 0, sizeof(THREAD_OPTS));
	memset(&m_UpdateThreadOpts, 0, sizeof(THREAD_OPTS));

	m_ReadThreadOpts._this = (void*)this;
	m_ReadThreadOpts.thread_type = ETP_THREAD_READ;

	m_FramesThreadOpts._this = (void*)this;
	m_FramesThreadOpts.thread_type = ETP_THREAD_PROCESS;

	m_UpdateThreadOpts._this = (void*)this;
	m_UpdateThreadOpts.thread_type = ETP_THREAD_UPDATES;

	pthread_create(&m_UpdateThreadOpts.tid, NULL, &thread_func, (void*)&m_UpdateThreadOpts);
	while (m_UpdateThreadOpts.inited == 0)
	{
		usleep(1000*500);
		XBMC->Log(LOG_DEBUG, "[PVREtp] Waiting for m_UpdateThreadOpts.inited\n");
	}

	if (m_UseMemList)
	{
		uint8_t *tmp = NULL;
		m_MemList = new PLATFORM::SyncedBuffer<uint8_t*>(ETP_MEMLIST_COUNT);

		for(i=0; i < ETP_MEMLIST_PRE_ALLOC; i++)
		{
			tmp = (uint8_t *)malloc(m_TsBufferSize);
//			tmp = new uint8_t[m_TsBufferSize + sizeof(uint64_t) + 1]();
			m_MemList->Push(tmp);
		}
	}
}

PVREtp::~PVREtp(void)
{
	uint8_t *tmp = NULL;
	CloseLiveStream();
	DemuxFlush();

	if (m_ETPProto)
		delete m_ETPProto;
	m_ETPProto = NULL;

	if (m_demuxPacketBuffer)
		delete m_demuxPacketBuffer;
	m_demuxPacketBuffer = NULL;

	if (m_NetworkBuffer)
		delete m_NetworkBuffer;
	m_NetworkBuffer = NULL;

	if (m_UseMemList)
	{
		while (m_MemList->Pop(tmp))
			free(tmp);
	}

	while (m_UpdateThreadOpts.tid)
	{
		XBMC->Log(LOG_DEBUG, "[PVREtp] Waiting for m_UpdateThread to die\n");
		usleep(1000 * 1000);
	}

}

bool PVREtp::Ping(void)
{
	if (m_ETPProto)
		return m_ETPProto->Ping();
	return false;
}

void PVREtp::CloseLiveStream(void)
{
	uint8_t * _tmp = NULL;
	size_t tmpsz = 0;
	XBMC->Log(LOG_DEBUG, "[CloseLiveStream] CloseLiveStream Start\n");

	m_StreamDie = 1;
	m_usRemoteDataPort = 0;
	m_StartTime = 0;

	if (m_ETPProto)
		m_ETPProto->CloseStream();


	while (m_FramesThreadOpts.inited || m_ReadThreadOpts.inited)
	{
		XBMC->Log(LOG_DEBUG, "[CloseLiveStream] Need to wait m_FramesThreadOpts.inited: %d, m_ReadThreadOpts.inited: %d\n", m_FramesThreadOpts.inited, m_ReadThreadOpts.inited);
		usleep(1000*1000);
	}

	DemuxFlush();
	m_EtpStreamCount = 0;
	m_PidMapCount = 0;


	for(int i=0; i < PVR_STREAM_MAX_STREAMS; i++)
	{
		if (m_EtpStream[i].stream_buffer.buffer)
		{
			free(m_EtpStream[i].stream_buffer.buffer);
			m_EtpStream[i].stream_buffer.buffer = NULL;
			m_EtpStream[i].stream_buffer.alloc_size = 0;

//			_tmp = m_EtpStream[i].stream_buffer.buffer;
//			tmpsz = m_EtpStream[i].stream_buffer.alloc_size;
		}
//		memset(&m_EtpStream[i], 0, sizeof(ETP_STREAM_PACKET));
		m_EtpStream[i].stream_buffer.buffer_pos = 0;
		m_EtpStream[i].pid = 0;

		memset(&m_PidMap[i], 0, sizeof(ETP_PID_MAP));

		if (_tmp && tmpsz)
		{
			m_EtpStream[i].stream_buffer.buffer = _tmp;
			m_EtpStream[i].stream_buffer.alloc_size = tmpsz;
		}
	}
}

void PVREtp::PauseStream(bool bPaused)
{
	m_Paused = bPaused;
	XBMC->Log(LOG_DEBUG, "[PauseStream] m_Paused: %d,  bPaused: %d\n", m_Paused, bPaused);
}

bool PVREtp::CanSeekStream(void)
{
	return true;
}

bool PVREtp::CanPauseStream(void)
{
	return true;
}

bool PVREtp::SeekTime(int time, bool backward, double *startpts)
{
	bool ret = false;
	uint64_t new_time=0, resp=0;

/*
	XBMC->Log(LOG_DEBUG, "[SeekTime] NOW: %ld -- Open: %ld\n", _now, m_StreamOpenTime);
	if (_now - m_StreamOpenTime <= 6)
	{
		XBMC->Log(LOG_DEBUG, "[SeeKTime] ------------ Too Soon From Opentime\n");
		return false;
	}
*/
	if (m_Paused)
	{
		XBMC->Log(LOG_DEBUG, "[SeeKTime] Paused, no seek\n");
		return false;
	}

	if (time < 0 && backward == 0)
	{
		XBMC->Log(LOG_DEBUG, "[SeeKTime] Time is negative, no seek\n");
		return false;
	}

	if (!StreamAlive())
	{
		XBMC->Log(LOG_DEBUG, "[SeeKTime] Stream is not alive :(\n");
		return false;
	}

	m_Seeking = 1;
	m_SeekedProcessACK = 0;
	m_SeekedReadACK = 0;

	while (m_SeekedProcessACK == 0 || m_SeekedReadACK == 0)
	{
//		XBMC->Log(LOG_DEBUG, "[SeeKTime] Waiting for read/process thread sync, m_SeekedProcessACK: %d -- m_SeekedReadACK: %d\n", m_SeekedProcessACK, m_SeekedReadACK);
		usleep(100 * 1000);
	}
	new_time = floor(((double)time * 90.0) + m_StartTime);
//	XBMC->Log(LOG_DEBUG, "[SeeKTime] ------------------- SeekTime: %d (%0.2f), back: %d -- seekto: %" PRId64", m_StartTime: %0.2f, total: %0.2f\n", time, (time*1.0)/1000.0, backward, new_time, m_StartTime/90000.0, ((time*1.0)/1000.0) - (m_StartTime/90000.0));


	m_EndTime = m_ETPProto->StreamGetTimeEnd();
	if (new_time >= m_EndTime)
	{
		new_time = m_EndTime - (90000 * m_StreamOpenWaitTime);
//		XBMC->Log(LOG_DEBUG, "[SeeKTime] --- Setting seektime to live, req: %" PRId64", end: %" PRId64"\n", new_time, m_EndTime); 
	}

	resp = m_ETPProto->StreamSetTimePos(new_time);

	if ((int64_t)resp == -1)
	{
		XBMC->Log(LOG_DEBUG, "[SeeKTime] -------------- SeekTime, seek_set bad\n");
		m_Seeking = 0;
	}
	else
	{
		m_LastSeekTime = PTS_to_DVD(resp);
		XBMC->Log(LOG_DEBUG, "[SeeKTime] Sendin Time Position: %" PRId64" (%0.2f), Ret TPos: %" PRId64" (%0.2f) -- m_StartTime: %" PRId64" (%0.2f)\n", new_time, new_time/90000.0, resp, resp/90000.0, m_StartTime, m_StartTime/90000.0);
//		XBMC->Log(LOG_DEBUG, "[SeeKTime] Return Time Position: %" PRId64" (%0.2f), LastSeekTime: %" PRId64"\n", resp, resp/90000.0, m_LastSeekTime);
		if (startpts)
			*startpts = DVD_NOPTS_VALUE;
		if (m_bDoDemux)
			EtpResetStream();

		DemuxFlush();
		ret = true;
	}
	m_ETPProto->StreamSetTimePosDone(ret);
	return ret;
}

long long PVREtp::SeekLiveStream(long long iPosition, int iWhence)
{
	XBMC->Log(LOG_DEBUG, "[SeekLiveStream] iPos: %lld, iWhence: %d\n", iPosition, iWhence);
	return 1;
}

long long PVREtp::PositionLiveStream(void)
{
	if (StreamAlive())
	{
		m_CurrentTime = m_ETPProto->StreamGetTimePos();
		XBMC->Log(LOG_DEBUG, "[PositionLiveStream] CurrentTime: %" PRId64"\n", m_CurrentTime);
		return (long long)floor(PTS_to_DVD(m_CurrentTime - m_StartTime));
	}
	return -1;
}

long long PVREtp::LengthLiveStream(void)
{
	time_t now = TimeNow();

	if (now - m_LastEndTimeUpdate < 1)
	{
		return (long long)((m_EndTime - m_StartTime));
	}

	if (StreamAlive())
	{
		uint32_t count = 0;
		GetStartTime();
		m_LastEndTimeUpdate = now;

		do
		{
			m_EndTime = m_ETPProto->StreamGetTimeEnd();

			if (m_EndTime == ETP_INVALID_TIME)
			{
				usleep(1000*250);
				count++;
				if (count > 4 || m_StreamDie)
				{
					XBMC->Log(LOG_DEBUG, "[LengthLiveStream] EndTime is invalid, exiting\n");
					return -1;
				}
				XBMC->Log(LOG_DEBUG, "[LengthLiveStream] EndTime is invalid, retry\n");

			}
		} while(m_EndTime == ETP_INVALID_TIME);

		if (m_EndTime > m_StartTime)
		{
			return (long long)((m_EndTime - m_StartTime));
		}
	}
	return -1;
}

bool PVREtp::SyncWait(uint32_t waittime)
{
	uint64_t last_pts = 0;
	uint32_t count = 0;

	PositionLiveStream();

	while(!m_StartTime)
	{
		GetStartTime();
		XBMC->Log(LOG_DEBUG, "[SyncWait] m_StartTime: %" PRId64"\n", m_StartTime);
		usleep(1000*1000);
		count++;
		if (count > 5)
		{
			XBMC->Log(LOG_DEBUG, "[SyncWait]\t ---------- SyncWait, close stream\n");
			CloseLiveStream();
			return false;
		}
	}

	count = 0;
	LengthLiveStream();

	do
	{
		last_pts = m_EndTime;
		m_EndTime = m_ETPProto->StreamGetTimeEnd();
//		XBMC->Log(LOG_DEBUG, "[SyncWait] Start: %" PRId64", End: %" PRId64", Current: %" PRId64" [diff: %" PRId64" (%0.2f)]\n", m_StartTime, m_EndTime, m_CurrentTime, m_EndTime - m_CurrentTime, (m_EndTime - m_CurrentTime)/90000.0);
		if (last_pts == m_EndTime)
			count++;
		else
			count = 0;

		if (count > 10)
		{
			XBMC->Log(LOG_DEBUG, "[SyncWait] \t ----------- Closing here1\n");
			CloseLiveStream();
			return false;
		}
		else
			usleep(500*1000);

	} while((m_EndTime - m_CurrentTime)/90000 < waittime);

	return true;
}

bool PVREtp::OpenLiveStream(const PVR_CHANNEL &channel)
{
	bool ret = false;
	XBMC->Log(LOG_DEBUG, "[OpenLiveStream] Tune Channel: %d\n", channel.iUniqueId);

	if (m_ETPProto)
	{
		if (m_ETPProto->TuneStream(channel.iUniqueId) == true)
		{
			m_StreamIsRecording = false;

			if (StreamOpenNext())
			{
//				if (SyncWait(m_StreamOpenWaitTime))
					ret = true;
			}
		}
	}
	return ret;
}

int PVREtp::ReadLiveStream(unsigned char *pBuffer, unsigned int iBufferSize)
{
	ssize_t rc = 0;
	uint32_t count = 0;
	uint8_t skip = 0;

//	XBMC->Log(LOG_DEBUG, "ReadliveStream\n");
	if (pBuffer && iBufferSize > 0 && StreamAlive())
	{
		if ((iBufferSize % TS_PACKET_SIZE) == 0)
		{
			pBuffer[0] = 0;
			while (pBuffer[0] != TS_SYNC_BYTE)
			{
				if (count > 0)
				{
					XBMC->Log(LOG_DEBUG, "[ReadLiveStream] Sync..\n");
				}
				rc = m_ETPProto->ReadBytesFromStream(pBuffer, 1);

				if (rc <= 0)
				{
//					XBMC->Log(LOG_DEBUG, "[ReadLiveStream] Error 1: %" PRId64" (count: %d)\n", rc, count);
					return rc;
				}
				count++;
				if (count + 188 == iBufferSize)
				{
					XBMC->Log(LOG_DEBUG, "[ReadLiveStream] Error 2\n");
					return -2;
				}
			}
			skip = 1;
		}
		rc = m_ETPProto->ReadBytesFromStream(pBuffer + skip, iBufferSize - skip);
		if (rc <= 0)
		{
			XBMC->Log(LOG_DEBUG, "[ReadLiveStream]\tPVREtp::ReadLiveStream, ReadBytesFromStream ret: %" PRId64"\n", rc);
			return rc;
		}
 	}
	return rc + skip;
}

int PVREtp::GetChannelsAmount(void)
{
	int count = 0;

	if (m_ETPProto)
		count = m_ETPProto->GetChannelCount();

	XBMC->Log(LOG_DEBUG, "[GetChannelsAmount] Count: %d\n", count);
	return count;
}


PVR_ERROR PVREtp::GetChannels(ADDON_HANDLE handle, bool bRadio)
{
	PVR_ERROR ret = PVR_ERROR_SERVER_TIMEOUT;

	if (bRadio)
		return PVR_ERROR_NOT_IMPLEMENTED;

	if (m_ETPProto)
	{
		if (m_ETPProto->GetChannelList() == true)
		{
			PVR_CHANNEL xbmcChannel;
			int count = 1;
			PROTOCOL_CHANNEL_STRUCT p_channel;
			memset(&p_channel, 0, sizeof(p_channel));
			ret = PVR_ERROR_NO_ERROR;

			while(p_channel.is_last == 0)
			{
 				memset(&xbmcChannel, 0, sizeof(PVR_CHANNEL));
				if (m_ETPProto->GetChannelListNext(&p_channel) == true)
				{
//					XBMC->Log(LOG_DEBUG, "Channel %d, ICO: %s, Name: %s (%d)\n", p_channel.num, p_channel.icon_path, p_channel.name, p_channel.is_last);
					snprintf(xbmcChannel.strChannelName, PVR_ADDON_NAME_STRING_LENGTH-1, "%s / %d", p_channel.name, p_channel.num);
//					strncpy(xbmcChannel.strChannelName, p_channel.name, PVR_ADDON_NAME_STRING_LENGTH);
//					xbmcChannel.iChannelNumber = p_channel.num;
					xbmcChannel.iChannelNumber = count++;
					xbmcChannel.iUniqueId = p_channel.num;
					strncpy(xbmcChannel.strIconPath, p_channel.icon_path, PVR_ADDON_URL_STRING_LENGTH);
//					xbmcChannel.p_channel.icon_path
					PVR->TransferChannelEntry(handle, &xbmcChannel);
				}
				else
				{
					XBMC->Log(LOG_DEBUG, "[GetChannels] BLEAH!\n");
					ret = PVR_ERROR_FAILED;
					break;
				}
			}
			m_ETPProto->GetChannelListDone();
		}
	}
	return ret;
}

int PVREtp::GetGenreFromStr(char * genre, char *subgenre)
{
	if (genre)
	{
		if (strcmp(genre, "News") == 0)
			return EPG_EVENT_CONTENTMASK_NEWSCURRENTAFFAIRS;
		else if (strcmp(genre, "Sports") == 0)
			return EPG_EVENT_CONTENTMASK_SPORTS;
		else if (strcmp(genre, "Children") == 0)
			return EPG_EVENT_CONTENTMASK_CHILDRENYOUTH;
		else if (strcmp(genre, "Movie") == 0)
			return EPG_EVENT_CONTENTMASK_MOVIEDRAMA;
		else if (strcmp(genre, "Music") == 0)
			return EPG_EVENT_CONTENTMASK_MUSICBALLETDANCE;
		else if (strcmp(genre, "Educational") == 0)
			return EPG_EVENT_CONTENTMASK_EDUCATIONALSCIENCE;
		else if (strcmp(genre, "Special") == 0)
			return EPG_EVENT_CONTENTMASK_SPECIAL;
		else if (strcmp(genre, "Paid Programming") == 0)
			return EPG_EVENT_CONTENTMASK_UNDEFINED;
		else if (subgenre && strcmp(subgenre, "Episodic") == 0)
			return EPG_EVENT_CONTENTMASK_SHOW;
		else
			return EPG_EVENT_CONTENTMASK_USERDEFINED;
	}
	return -1;
}

PVR_ERROR PVREtp::GetEPGForChannel(ADDON_HANDLE handle, const PVR_CHANNEL &channel, time_t iStart, time_t iEnd)
{
	PVR_ERROR ret = PVR_ERROR_SERVER_TIMEOUT;

	if (m_ETPProto)
	{
		uint64_t count = 0, _count = 0, skipped = 0;
		char tmp[MAX_STR_SZ] = {0};
		char *genre = NULL, *subgenre=NULL;

		snprintf(tmp, MAX_STR_SZ-1, "%d", channel.iUniqueId);
		count = m_ETPProto->GetEPG(tmp);
//		XBMC->Log(LOG_DEBUG, "[GetEPGForChannel] Channel: %s (start: %" PRId64 " end: %" PRId64") (count: %" PRId64") .. ", tmp, iStart, iEnd, count);
		XBMC->Log(LOG_DEBUG, "[GetEPGForChannel] Channel: %s, count: %" PRId64", timer_send sz: %d", tmp, count, sizeof(TIMER_STRUCT_SEND));
		if (count > 0)
		{
			int i, j;
	        EPG_TAG tag;
			TIMER_STRUCT timer = {0};
			TIMER_STRUCT_EXTRA extra = {0};

			while(_count < count && timer.data.is_last == 0)
			{
		        memset(&tag, 0, sizeof(EPG_TAG));
				memset(tmp, 0, MAX_STR_SZ);

				if (m_ETPProto->GetEPGNext(&timer.data, &extra) == true)
				{
					_count++;
					if (timer.data.start_epoch < iStart && timer.data.end_epoch < iStart)
					{
						skipped++;
						XBMC->Log(LOG_DEBUG, "Skipping EPG Entry, based on start: %" PRId64 "\n", timer.data.start_epoch);
						continue;
					}

					if (timer.data.end_epoch > iStart && timer.data.start_epoch > iEnd)
					{
						skipped++;
						XBMC->Log(LOG_DEBUG, "Skipping EPG Entry, based on end: %" PRId64 "\n", timer.data.end_epoch);
						continue;
					}
	    		    tag.startTime          = timer.data.start_epoch;
		    	    tag.endTime            = timer.data.end_epoch;

					for(j=0; j < GENRE_MAIN_COUNT; j++)
					{
						for(i=0; i < extra.category_count; i++)
						{
//							XBMC->Log(LOG_DEBUG, "H: %s -- %s\n", GENRE_MAIN[j], extra.category[i]);
							if (strcmp(extra.category[i],  GENRE_MAIN[j]) == 0)
							{
								genre = strdup(extra.category[i]);
								memset(extra.category[i], 0, TIEMR_STRUCT_MAX_STRLEN);
								break;
							}
						}

						if (genre)
							break;
					}

					if (genre)
					{
						for(j=0; j < GENRE_SUB_COUNT; j++)
						{
							for(i=0; i < extra.category_count; i++)
							{
								if (extra.category[i][0] == 0)
									continue;

								if (strcmp(extra.category[i], GENRE_SUB[j]) == 0)
								{
									subgenre = strdup(extra.category[i]);
									break;
								}
							}

							if (subgenre)
								break;
						}
					}

					if (genre)
					{
						tag.iGenreSubType = 0;
						tag.iGenreType = GetGenreFromStr(genre, subgenre);
						if (tag.iGenreType == -1)
						{
							tag.iGenreType = EPG_EVENT_CONTENTMASK_USERDEFINED;
							tag.strGenreDescription = strdup(genre);
						}
						free(genre);
					}

					if (subgenre)
						free(subgenre);

					genre = subgenre = NULL;

					snprintf(tmp, MAX_STR_SZ-1, "%" PRId64"%" PRId64"%s%d", timer.data.start_epoch, timer.data.end_epoch, timer.data.title, channel.iChannelNumber);
					XBMC->Log(LOG_DEBUG, "TMP1: %s\n", tmp);

					size_t plotsz = 512;
					char *newplot = new char[plotsz]();

					tag.iUniqueBroadcastId = str_hash(tmp);
					tag.strTitle           = strdup(timer.data.title);
					tag.iChannelNumber     = channel.iChannelNumber;
					tag.startTime          = timer.data.start_epoch;
					tag.endTime            = timer.data.end_epoch;
					tag.strPlotOutline     = strdup(timer.data.plot_outline);

					if (newplot)
					{
						int plot_len = strlen(timer.data.plot);
						int plot_outline_len = strlen(timer.data.plot_outline);


						if (plot_outline_len > 1 && plot_len > 1)
							snprintf(newplot, plotsz, "%s\n\n%s", timer.data.plot_outline, timer.data.plot);
						else if (plot_outline_len > 1)
							snprintf(newplot, plotsz, "%s", timer.data.plot_outline);
						else if (plot_len > 1)
							snprintf(newplot, plotsz, "%s", timer.data.plot);
						else
							snprintf(newplot, plotsz, "%s", timer.data.title);

						tag.strPlot = newplot;
					}
					else
					{
						tag.strPlot = strdup(timer.data.plot);
					}
					XBMC->Log(LOG_DEBUG, "EPG Plot: %s\n", tag.strPlot);
					PVR->TransferEpgEntry(handle, &tag);

					if (newplot)
						delete newplot;
				}
				else
				{
					XBMC->Log(LOG_DEBUG, "[GetEPGForChannel] GetEPGNext returned NULL?");
					break;
				}
			}

			m_ETPProto->GetEPGDone();
			if (_count == count)
			{
				XBMC->Log(LOG_DEBUG, "[GetEPGForChannel] Good! Skipped: %" PRId64"\n", skipped);
				ret = PVR_ERROR_NO_ERROR;
			}
			else
			{
				XBMC->Log(LOG_DEBUG, "[GetEPGForChannel] Bad: %" PRId64" -- %"PRId64"\n", _count, count);
			}
		}
		else
		{
			XBMC->Log(LOG_DEBUG, "[GetEPGForChannel] Count is ZERO!\n");
		}
	}
	return ret;
}

int PVREtp::GetRecordingsAmount(void)
{
	m_RecCount = 0;
	if (m_ETPProto)
		m_RecCount = m_ETPProto->GetRecordingCount();
	return m_RecCount;
}

PVR_ERROR PVREtp::GetRecordings(ADDON_HANDLE handle)
{
	PVR_ERROR ret = PVR_ERROR_SERVER_TIMEOUT;
	PLATFORM::CLockObject lock(m_RecUpdateLock);

	if (m_ETPProto)
	{
		PVR_RECORDING *tmp = NULL;
		uint32_t count = 0;
		if (GetRecordingsAmount() == 0)
		{
			XBMC->Log(LOG_DEBUG, "[GetRecordings] No Recordings\n");
			m_RecSeqNum = m_ETPProto->GetRecordingSeqNum();
			while (m_RecCache->Pop(tmp))
				delete tmp;

			m_RecTriggered = 1;
			return PVR_ERROR_NO_ERROR;
		}

//		XBMC->Log(LOG_DEBUG, "Rec cache: %" PRId64"\n", m_RecCache->Size());
		if (m_RecCache && m_RecCache->Size())
		{
			//uint64_t seq = m_ETPProto->GetRecordingSeqNum();

			if (m_ETPProto->GetRecordingSeqNum() == m_RecSeqNum)
			{
				tmp = new PVR_RECORDING();
				tmp->iLifetime = -1;
				tmp->iPriority = -2;
				tmp->iDuration = -3;

				m_RecCache->Push(tmp);
				while (m_RecCache->Pop(tmp))
				{
//					XBMC->Log(LOG_DEBUG, "GetRecs cache: %d %d %d\n", tmp->iLifetime, tmp->iPriority, tmp->iDuration);
					if (tmp->iLifetime == -1 && tmp->iPriority == -2 && tmp->iDuration == -3)
					{
						m_RecTriggered = 1;
						XBMC->Log(LOG_DEBUG, "[GetRecordings] -- sending back cache\n");
			 			return PVR_ERROR_NO_ERROR;
					}

					m_RecCache->Push(tmp);
					PVR->TransferRecordingEntry(handle, tmp);
				}
			}
			else
			{
//				XBMC->Log(LOG_DEBUG, "[GetRecordings] -- emptying cache\n");

				while (m_RecCache->Pop(tmp))
					delete tmp;
			}
		}


		count = m_ETPProto->GetRecordingList();

		if (count > 0)
		{
			PVR_RECORDING xbmcRecording;
			TIMER_STRUCT timer = {0};
			uint32_t _count = 0;
			memset(&timer, 0, sizeof(timer));
			ret = PVR_ERROR_NO_ERROR;

			while(_count < count)
			{
				memset(&xbmcRecording, 0, sizeof(xbmcRecording));
				if (m_ETPProto->GetRecordingListNext(&timer.data) == true)
				{
					PVR_RECORDING *cache = new PVR_RECORDING();

					snprintf(xbmcRecording.strRecordingId, PVR_ADDON_NAME_STRING_LENGTH -1, "%" PRId64"_%d_%" PRId64"", timer.data.channel_num, timer.data.id, timer.data.start_epoch);
					snprintf(xbmcRecording.strTitle, PVR_ADDON_NAME_STRING_LENGTH -1, "%s", timer.data.title);
					snprintf(xbmcRecording.strStreamURL, PVR_ADDON_NAME_STRING_LENGTH -1, "pvr://recordings/%d", timer.data.id);
					snprintf(xbmcRecording.strPlotOutline, PVR_ADDON_NAME_STRING_LENGTH -1, "%s", timer.data.plot_outline);
					snprintf(xbmcRecording.strPlot, PVR_ADDON_NAME_STRING_LENGTH -1, "%s", timer.data.plot);
					snprintf(xbmcRecording.strChannelName, PVR_ADDON_NAME_STRING_LENGTH -1, "%s", timer.data.channel_name);
					xbmcRecording.recordingTime = timer.data.start_epoch;
					xbmcRecording.iGenreType = GetGenreFromStr(timer.data.genre, timer.data.sub_genre);
/*
				XBMC->Log(LOG_DEBUG, "[%" PRId64"],[%d],[%" PRId64"],[%" PRId64"],[%s],[%s],[%s],[%s],[%s],[%s]\n", 
timer.data.channel_num, timer.data.id, timer.data.start_epoch, timer.data.end_epoch, 
timer.channel_name, timer.data.title, timer.data.plot_outline, timer.data.plot, timer.data.genre, timer.data.sub_genre);

*/

//					XBMC->Log(LOG_DEBUG, "REC_ID: %s\n", xbmcRecording.strRecordingId);
//					XBMC->Log(LOG_DEBUG, "\ttitle: %s\n", xbmcRecording.strTitle);
//					XBMC->Log(LOG_DEBUG, "\tStreamURL: %s\n", xbmcRecording.strStreamURL);
//					XBMC->Log(LOG_DEBUG, "\tPlotOutline: %s\n", xbmcRecording.strPlotOutline);
//					XBMC->Log(LOG_DEBUG, "\tPlot: %s\n", xbmcRecording.strPlot);
//					XBMC->Log(LOG_DEBUG, "\tChName: %s\n", xbmcRecording.strChannelName);
//					XBMC->Log(LOG_DEBUG, "\tStart Time: %" PRId64"\n", xbmcRecording.recordingTime);
//					XBMC->Log(LOG_DEBUG, "\tGenre: %d (%s)\n", xbmcRecording.iGenreType, timer.data.genre);
					memcpy(cache, &xbmcRecording, sizeof(PVR_RECORDING));
					m_RecCache->Push(cache);
					PVR->TransferRecordingEntry(handle, &xbmcRecording);
					_count++;
				}
				else
				{
					XBMC->Log(LOG_DEBUG, "[GetRecordings] BLEAH2\n");
					ret = PVR_ERROR_FAILED;
					break;
				}
			}

			m_RecSeqNum = m_ETPProto->GetRecordingSeqNum();

			if (_count < count)
				ret = PVR_ERROR_FAILED;

		}
		else
		{
			XBMC->Log(LOG_DEBUG, "[GetRecordings] sucks\n");
		}

		m_ETPProto->GetRecordingListDone();
	}
	m_RecTriggered = 1;

	return ret;
}

DemuxPacket* PVREtp::DemuxRead()
{
	DemuxPacket *ret = NULL;

	if (m_StreamIsRecording)
	{
		if (m_NullPacketCount)
		{
			XBMC->Log(LOG_DEBUG, "NEaring end of stream, demuxPacketSize: %" PRId64" -- NetBufSize: %" PRId64"\n", m_demuxPacketBuffer->Size(), m_NetworkBuffer->Size());
			if (m_demuxPacketBuffer->Size() == 0)
			{
//				XBMC->Log(LOG_DEBUG, "Sending null frames\n");
				usleep(1000*250);
				return NULL;
			}
		}
	}

	if (m_StartTime && m_demuxPacketBuffer->Pop(ret, 64))
	{
		ETP_STREAM_PACKET *stream_packet = EtpGetStream(m_EtpStream[ret->iStreamId].pid);
//		XBMC->Log(LOG_DEBUG, "Demux 3\n");

		if (stream_packet)
		{
			if (m_VPushed == 0 && stream_packet->map->type == MPEGTS_TYPE_AUDIO)
			{
//				XBMC->Log(LOG_DEBUG, "[DemuxRead] Waiting for video frame to be pushed\n");
				PVR->FreeDemuxPacket(ret);
				return PVR->AllocateDemuxPacket(0);
			}
			else if(stream_packet->map->type == MPEGTS_TYPE_VIDEO)
				m_VPushed++;

/*
			if (m_APushed == 0 && stream_packet->map->type == MPEGTS_TYPE_VIDEO)
			{
				XBMC->Log(LOG_DEBUG, "[DemuxRead] Waiting for audio frame to be pushed\n");
				PVR->FreeDemuxPacket(ret);
				return PVR->AllocateDemuxPacket(0);
			}
			else if(stream_packet->map->type == MPEGTS_TYPE_AUDIO)
				m_APushed++;
*/

//			XBMC->Log(LOG_DEBUG, "demuxread, pts: %0.2f, dts: %0.2f, last: %" PRId64"\n",  ret->pts / DVD_TIME_BASE, ret->dts /DVD_TIME_BASE, m_LastSeekTime - m_StartTime);
			if (stream_packet->need_sync && m_LastSeekTime)
			{
//				double tmp1 = (double)m_LastSeekTime - m_StartTime/90);
				double diff = (fabs((double)(m_LastSeekTime - PTS_to_DVD(m_StartTime)) - ret->pts))/DVD_TIME_BASE;
				if (diff < m_StreamOpenWaitTime)
				{
					stream_packet->need_sync = 0;
				}
				else
				{
//					XBMC->Log(LOG_DEBUG, "Sync free\n");
					PVR->FreeDemuxPacket(ret);
					ret = NULL;
				}
			}
//		XBMC->Log(LOG_DEBUG, "Demux 4\n");

			if (m_Props.iStreamCount && m_StreamProcessCount < m_Props.iStreamCount)
			{
				int i;
				for(i=0; i < ETP_MAX_PID_COUNT; i++)
				{
					if (m_StreamProcessMsg[i] == stream_packet->pid)
						break;
				}

				if (i >= ETP_MAX_PID_COUNT)
				{
					XBMC->Log(LOG_DEBUG, "[DemuxRead]\tPID: 0x%x (%d) (%d)\n", stream_packet->pid, stream_packet->pid, m_StreamProcessCount);
					for(i=0; i < ETP_MAX_PID_COUNT; i++)
					{
						if (m_StreamProcessMsg[i] == 0)
						{
							m_StreamProcessCount++;
							m_StreamProcessMsg[i] = stream_packet->pid;
							break;
						}
					}
				}

			}

/*
			if (stream_packet->map->type == MPEGTS_TYPE_VIDEO && ret->pts == ret->dts)
				XBMC->Log(LOG_DEBUG, "DemuxRead Video: %0.2f\n", ret->pts);
*/
//		XBMC->Log(LOG_DEBUG, "Demux 5\n");

		}
		else
		{
			if (ret && ret->iStreamId == DMX_SPECIALID_STREAMCHANGE || m_Paused)
			{
			}
			else
			{
				XBMC->Log(LOG_DEBUG, "[DemuxRead] JJJJJJJJJJJJJJJJJJJ\n");
			}
		}
	}
	else
	{
		int count = 0;
//		XBMC->Log(LOG_DEBUG, "Demux 6\n");
		while (!m_StreamDie && m_demuxPacketBuffer->Size() < 2)
		{
//				XBMC->Log(LOG_DEBUG, "DemuxRwad, sleeping: %d (%" PRId64")\n", count, m_demuxPacketBuffer->Size());
			usleep(32 * 1000);
			count++;
			if (count == 9)
			{
				count = 0;
//				break;
			}
		}
//		XBMC->Log(LOG_DEBUG, "Demux 7\n");

	}
//		XBMC->Log(LOG_DEBUG, "Demux 9\n");
	if (!ret)
	{
		if (!m_StreamDie)
			ret = PVR->AllocateDemuxPacket(0);
//		else
//			XBMC->Log(LOG_DEBUG, "[DemuxRead] Die!!!\n");
	}
//	if (!ret)
//		XBMC->Log(LOG_DEBUG, "[DemuxRead] Ret is null?\n");

	return ret;
}

void PVREtp::DemuxReset(void)
{
	XBMC->Log(LOG_DEBUG, "[DemuxReset] I was Called\n");
}

void PVREtp::DemuxFlush(void)
{
//	XBMC->Log(LOG_DEBUG, "[DemuxFlush] start\n");

	DemuxPacket *dp = NULL;
	uint8_t *_TsBuffer = NULL;

	while (m_demuxPacketBuffer->Pop(dp))
		PVR->FreeDemuxPacket(dp);

	while (m_NetworkBuffer->Pop(_TsBuffer))
	{
		if (!m_UseMemList || m_MemList->Push(_TsBuffer) == false)
			free(_TsBuffer);
	}

	m_WaitForVideoFrame = 1;
	m_Seeking = 0;

	XBMC->Log(LOG_DEBUG, "[DemuxFlush] done\n");

}

void PVREtp::DemuxAbort(void)
{
	XBMC->Log(LOG_DEBUG, "PVREtp::DemuxAbort\n");
	m_StreamDie = 1;
//	EtpResetStream();
//	PidsClear();
//	DemuxFlush();

}

PVR_ERROR PVREtp::GetStreamProperties(PVR_STREAM_PROPERTIES* props)
{
//	XBMC->Log(LOG_DEBUG, "[GetStreamProperties] Start\n");

//	int count = 0;
/*
	while(m_PacketCount < 5)
	{
		XBMC->Log(LOG_DEBUG, "GetStreamProperties waiting for data 2\n");
		usleep(1000*1000);
		count++;
		if (count > 5)
			return PVR_ERROR_SERVER_TIMEOUT;
	}
*/
	if (props)
	{
		memset(props, 0, sizeof(PVR_STREAM_PROPERTIES));
		if (m_ETPProto)
		{
			ETP_PID_MAP * map = NULL;
			TUNER_STREAM_STRUCT stream;

			if (m_ETPProto->GetStreamInfo() == false)
				return PVR_ERROR_FAILED;

			do
			{
				memset(&stream, 0, sizeof(TUNER_STREAM_STRUCT));
				if (m_ETPProto->GetStreamInfoNext(&stream) == false)
				{
					XBMC->Log(LOG_DEBUG, "[GetStreamProperties] Triste\n");
					break;
				}

				map = PidAdd(stream.pid);
				props->stream[props->iStreamCount].iPhysicalId = props->iStreamCount;
				props->stream[props->iStreamCount].iBitRate = stream.bitrate;

				if (stream.type == MPEGTS_STREAM_TYPE_ISO13818_V || stream.type == MPEGTS_STREAM_TYPE_ISO14496_10)
				{
					double tmp_fps = 0;

					map->type = MPEGTS_TYPE_VIDEO;

					props->stream[props->iStreamCount].iCodecType = AVMEDIA_TYPE_VIDEO;
					if (stream.type == MPEGTS_STREAM_TYPE_ISO13818_V)
						props->stream[props->iStreamCount].iCodecId = CODEC_ID_MPEG2VIDEO;
					else if (stream.type == MPEGTS_STREAM_TYPE_ISO14496_10)
						props->stream[props->iStreamCount].iCodecId = CODEC_ID_H264;

					props->stream[props->iStreamCount].iHeight = stream.height;
					props->stream[props->iStreamCount].iWidth = stream.width;

					if (stream.fps == 1 || stream.fps == 4 || stream.fps == 7)
					{
						props->stream[props->iStreamCount].iFPSScale = 1001;
						if (stream.fps == 1)
							tmp_fps = 24000;
						else if (stream.fps == 4)
							tmp_fps = 30000;
						else if (stream.fps == 7)
							tmp_fps = 60000;
					}
					else
					{
						props->stream[props->iStreamCount].iFPSScale = 1000;
						tmp_fps = floor(ETP_FPS_INDEX[stream.fps] * 1000.0);
					}

					props->stream[props->iStreamCount].iFPSRate = (int)tmp_fps;
					props->stream[props->iStreamCount].fAspect = ETP_ASPECT_INDEX[stream.aspect];

					if (map)
					{
						map->duration = (1.0/(double)ETP_FPS_INDEX[stream.fps]);
						map->duration *= (DVD_TIME_BASE);
					}
//					XBMC->Log(LOG_DEBUG, "[%d] vPID: 0x%x (type: 0x%x), h: %d, w: %d, fps1: %d, fps2: %d, aspect: %0.4f (dur: %0.2f [0x%x]) -- Max FrmCnt: %d\n", props->iStreamCount, stream.pid, stream.type, props->stream[props->iStreamCount].iHeight, props->stream[props->iStreamCount].iWidth, props->stream[props->iStreamCount].iFPSScale, props->stream[props->iStreamCount].iFPSRate, props->stream[props->iStreamCount].fAspect, map->duration, map->pid, m_MaxFrameCount);
					XBMC->Log(LOG_DEBUG, "[GetStreamProperties] [%d] vPID: 0x%x (type: 0x%x), (%dx%d), fps: %0.2f, aspect: %0.4f (dur: %0.2f)\n", 
							props->iStreamCount, stream.pid, stream.type, props->stream[props->iStreamCount].iWidth, 
							props->stream[props->iStreamCount].iHeight, 
							(double)props->stream[props->iStreamCount].iFPSRate/(double)props->stream[props->iStreamCount].iFPSScale,
							props->stream[props->iStreamCount].fAspect, 
							map->duration);

				}
				else if (stream.type == MPEGTS_STREAM_TYPE_USER_DATA ||
							stream.type == MPEGTS_STREAM_TYPE_ISO11172_A ||
							stream.type == MPEGTS_STREAM_TYPE_ISO13818_3)
				{
					int x;
					map->type = MPEGTS_TYPE_AUDIO;

					props->stream[props->iStreamCount].iCodecType  = AVMEDIA_TYPE_AUDIO;
					if (stream.type == MPEGTS_STREAM_TYPE_USER_DATA)
						props->stream[props->iStreamCount].iCodecId = CODEC_ID_AC3;
					else if (stream.type == MPEGTS_STREAM_TYPE_ISO13818_3)
						props->stream[props->iStreamCount].iCodecId = CODEC_ID_MP2;
					else if (stream.type == MPEGTS_STREAM_TYPE_ISO13818_7)
						props->stream[props->iStreamCount].iCodecId = CODEC_ID_AAC;

					props->stream[props->iStreamCount].iChannels = stream.channels;
					props->stream[props->iStreamCount].iSampleRate = stream.samplerate;

					for(x = 0; x < 4; x++)
					{
						props->stream[props->iStreamCount].strLanguage[x] = stream.iso639_lang[x];
					}

					if (map)
					{
						map->duration = (double)stream.frame_size/((double)stream.bitrate/8.0);
						map->duration *= (DVD_TIME_BASE);
						map->duration /= 1000;
					}
					XBMC->Log(LOG_DEBUG, "[GetStreamProperties] [%d] aPID: 0x%x (t: 0x%x), Ch: %d, sRate: %d, bRate: %d, FrmSz: %d (Dur: %0.2f), ISO639: %s\n", props->iStreamCount, stream.pid, stream.type, props->stream[props->iStreamCount].iChannels, props->stream[props->iStreamCount].iSampleRate, props->stream[props->iStreamCount].iBitRate, stream.frame_size, map->duration, props->stream[props->iStreamCount].strLanguage);
				}
				else
				{
					XBMC->Log(LOG_DEBUG, "[GetStreamProperties] PID has no type: 0x%x\n", stream.pid);
				}

				if (map)
				{
					map->frame_size = stream.frame_size;
					map->codec = props->stream[props->iStreamCount].iCodecId;
				}

				props->iStreamCount++;
				map = NULL;
			}
			while(stream.is_last == 0);

			props->stream[props->iStreamCount].iPhysicalId = props->iStreamCount;
			props->stream[props->iStreamCount].iCodecType = AVMEDIA_TYPE_SUBTITLE;
			props->stream[props->iStreamCount].iCodecId = CODEC_ID_NONE;

			props->stream[props->iStreamCount].strLanguage[0] = 'e';
			props->stream[props->iStreamCount].strLanguage[1] = 'n';
			props->stream[props->iStreamCount].strLanguage[2] = 'g';
			props->stream[props->iStreamCount].strLanguage[3] = 0;
//			props->stream[props->iStreamCount].strLanguage[0] = "ATSC 608 (Field 1)";

			props->iStreamCount++;

			m_ETPProto->GetStreamInfoDone();

			memcpy(&m_Props, props, sizeof(*props));
			XBMC->Log(LOG_DEBUG, "[GetStreamProperties] Done and good, stream count: %d\n", props->iStreamCount);
			if (props->iStreamCount)
				return PVR_ERROR_NO_ERROR;
		}
	}

	XBMC->Log(LOG_DEBUG, "[GetStreamProperties] BAD!!\n");
	return PVR_ERROR_SERVER_TIMEOUT;
}

//PVR_ERROR_SERVER_TIMEOUT

ETP_STREAM_PACKET *PVREtp::EtpGetStream(uint16_t pid)
{
	if (pid)
	{
		int i;
		for(i=0; i < PVR_STREAM_MAX_STREAMS; i++)
		{
//			XBMC->Log(LOG_DEBUG, "EtpGetStream: 0x%x (0x%x)\n", m_EtpStream[i].pid, pid);
			if (m_EtpStream[i].pid == pid)
			{
//				XBMC->Log(LOG_DEBUG, "EtpGetStream RET: 0x%x (0x%x)\n", m_EtpStream[i].pid, pid);
				return &m_EtpStream[i];
			}
		}
	}
	return NULL;
}

ETP_STREAM_PACKET *PVREtp::EtpAddStreamData(uint16_t pid, uint8_t *data, int data_sz)
{
	if (pid && data_sz && data)
	{
		ETP_STREAM_PACKET *e = EtpGetStream(pid);
		if (e && e->stream_buffer.buffer)
		{
			size_t _sz = 0;
//			XBMC->Log(LOG_DEBUG, "[0x%x] addstreamdata: %d\n", pid, e->map->frame_size);
			while (e->stream_buffer.buffer_pos + data_sz > e->stream_buffer.alloc_size)
			{
				_sz = ((e->stream_buffer.buffer_pos + data_sz)/e->stream_buffer.alloc_size) + 1;
				XBMC->Log(LOG_DEBUG, "[EtpAddStreamData] PID: 0x%x [pos: %" PRId64"] Realloc from: %" PRId64" to %" PRId64" (data_sz: %d)\n", pid, e->stream_buffer.buffer_pos, e->stream_buffer.alloc_size, e->stream_buffer.alloc_size * _sz, data_sz);
				e->stream_buffer.alloc_size *= _sz;
				e->stream_buffer.buffer = (uint8_t*)realloc((void*)e->stream_buffer.buffer, e->stream_buffer.alloc_size);
			}

			if (e->stream_buffer.buffer_pos + data_sz <= e->stream_buffer.alloc_size)
			{
				memcpy(e->stream_buffer.buffer + e->stream_buffer.buffer_pos, data, data_sz);
				e->stream_buffer.buffer_pos += data_sz;
				return e;
			}
			else
				XBMC->Log(LOG_DEBUG, "[EtpAddStreamData] frame too big\n");
		}
	}
	return NULL;
}

void PVREtp::EtpResetStream(void)
{
	int i;
	for(i=0; i < PVR_STREAM_MAX_STREAMS; i++)
	{
		if (m_EtpStream[i].pid != 0)
		{
			m_EtpStream[i].has_start = 0;
			m_EtpStream[i].need_sync = 1;
			m_EtpStream[i].stream_buffer.buffer_pos = 0;
			memset(&m_EtpStream[i].stream_buffer.timing, 0, sizeof(m_EtpStream[i].stream_buffer.timing));
		}
	}
	return;
}

ETP_STREAM_PACKET *PVREtp::EtpAddStream(uint16_t pid)
{
	int i;
	ETP_STREAM_PACKET *e = NULL;
	if (pid)
	{
		e = EtpGetStream(pid);

		if (!e)
		{
			for(i=0; i < PVR_STREAM_MAX_STREAMS-1; i++)
			{
				if (m_EtpStream[i].pid == 0)
				{
					m_EtpStream[i].pid = pid;
					m_EtpStreamCount++;
					e = &m_EtpStream[i];
					break;
				}
			}
		}
	}
	return e;
}

int PVREtp::EtpProcessStream(ETP_STREAM_PACKET *stream_packet)
{
	int frame_sz = -1, frame_start = -1, frame_end = -1;
	uint64_t pts = 0, dts = 0;
	DemuxPacket *dPacket = NULL;

	if (!stream_packet || !stream_packet->map)
	{
		XBMC->Log(LOG_DEBUG, "[EtpProcessStream] -- stream or map is null\n");
		return -1;
	}
	pts = stream_packet->stream_buffer.timing.pts;
	dts = stream_packet->stream_buffer.timing.dts;

	if (m_LastSeekTime)
	{
		uint64_t tmp = DVD_to_PTS(m_LastSeekTime);
		if (pts < tmp || (dts && dts < tmp))
		{
			stream_packet->stream_buffer.buffer_pos = 0;
//			XBMC->Log(LOG_DEBUG, "[EtpProcessStream] -- Wrong Packet Times, PTS: %0.2f, DTS: %0.2f -- lastseektime: %0.2f\n", pts/90000.0, dts/90000.0, tmp/90000.0);
			return -2;
		}
	}

//	XBMC->Log(LOG_DEBUG, "----- HMM, pts: %" PRId64" (%" PRId64") dts: %" PRId64", start: %" PRId64"\n", pts, pts - m_StartTime,dts, m_StartTime);

	if (pts < m_StartTime || (dts && dts < m_StartTime))
	{
//		XBMC->Log(LOG_DEBUG, "[EtpProcessStream] BAD1\n");
		stream_packet->stream_buffer.buffer_pos = 0;
		return -1;
	}

	frame_start = 0;
	frame_end = stream_packet->stream_buffer.buffer_pos;

	if (stream_packet->map->type == MPEGTS_TYPE_AUDIO)
	{
//		CodecDescriptor codecId = CodecDescriptor::GetCodecByISO13818(MPEGTS_STREAM_TYPE_USER_DATA);
//		if (codecId.Codec().codec_id == (xbmc_codec_id_t)stream_packet->map->codec)
		if (stream_packet->map->codec == CODEC_ID_AC3)
		{
			int ac3_peek = 0;

			if (stream_packet->stream_buffer.buffer_pos >= (size_t)stream_packet->map->frame_size)
			{
				pts = (stream_packet->frame_count * DVD_to_PTS(stream_packet->map->duration))  + stream_packet->map->first_pts;

				if (stream_packet->stream_buffer.buffer_pos > 4)
					ac3_peek = frame_end = PeekAC3FrameSize(stream_packet->stream_buffer.buffer, (uint32_t)stream_packet->stream_buffer.buffer_pos);
				else
					frame_end = stream_packet->map->frame_size;

				if (!(stream_packet->stream_buffer.buffer[0] == AC3_START_CODE[0] && stream_packet->stream_buffer.buffer[1] == AC3_START_CODE[1]))
				{
					int q;
					XBMC->Log(LOG_DEBUG, "[EtpProcessStream] Need Sync, PID: 0x%x\n", stream_packet->pid);
					for(q = 0; (size_t)q < stream_packet->stream_buffer.buffer_pos - 1; q++)
					{
						if(stream_packet->stream_buffer.buffer[q] == AC3_START_CODE[0] &&
							stream_packet->stream_buffer.buffer[q+1] == AC3_START_CODE[1])
						{
							ac3_peek = PeekAC3FrameSize(stream_packet->stream_buffer.buffer + q, (uint32_t)stream_packet->stream_buffer.buffer_pos);
							frame_start = q;
							frame_end = frame_start + ac3_peek;

//							XBMC->Log(LOG_DEBUG, "[0x%x] FOUND: %d [0x%x 0x%x] %" PRId64"\n", stream_packet->pid, q, stream_packet->stream_buffer.buffer[q], stream_packet->stream_buffer.buffer[q+1], stream_packet->stream_buffer.buffer_pos);
/*
							XBMC->Log(LOG_DEBUG, "[EtpProcessStream] [peek: %d] PID: 0x%x, Found in Buffer: %d [0x%x 0x%x] Current Buf. Pos: %" PRId64", Frame Start Pos: %d, Frame End Pos: %d\n", 
														ac3_peek,
														stream_packet->pid,
														q,
														stream_packet->stream_buffer.buffer[q],
														stream_packet->stream_buffer.buffer[q+1],
														stream_packet->stream_buffer.buffer_pos, frame_start, frame_end);
*/
							break;
						}
					}
				}
				else
				{
//					XBMC->Log(LOG_DEBUG, "[EtpProcessStream] -- NO Need to Sync, PID: 0x%x\n", stream_packet->pid);
				}

				if ((size_t)frame_end > stream_packet->stream_buffer.buffer_pos)
				{
//					XBMC->Log(LOG_DEBUG, "[EtpProcessStream] PID: 0x%x -- NO SYNC (start: %d, end: %d, pos: %" PRId64")\n", stream_packet->pid, frame_start, frame_end, stream_packet->stream_buffer.buffer_pos);
					if (frame_start)
					{
						stream_packet->stream_buffer.buffer_pos -= frame_start;
//						stream_packet->stream_buffer.buffer_pos = 0;;
						memmove(stream_packet->stream_buffer.buffer, stream_packet->stream_buffer.buffer + frame_start, stream_packet->stream_buffer.buffer_pos);
						if (stream_packet->stream_buffer.buffer[0] != AC3_START_CODE[0] || stream_packet->stream_buffer.buffer[1] != AC3_START_CODE[1])
						{
							XBMC->Log(LOG_DEBUG, "[EtpProcessStream] PID: 0x%x, After Move, 0x%x 0x%x, POS: %" PRId64"\n", stream_packet->pid, stream_packet->stream_buffer.buffer[0], stream_packet->stream_buffer.buffer[1], stream_packet->stream_buffer.buffer_pos);
						}
					}
					return -3;
				}
			}
		}
	}

	if (!frame_end || (frame_end <= frame_start))
	{
		XBMC->Log(LOG_DEBUG, "[EtpProcessStream] --------------------- FrameSize params [PID: 0x%x] %d %d\n", stream_packet->pid, frame_sz, frame_end);
		return -6;
	}

	frame_sz = frame_end - frame_start;

	if (frame_sz)
	{
		if (m_WaitForVideoFrame && stream_packet->map->type == MPEGTS_TYPE_VIDEO)
		{
			if (stream_packet->map->codec == MPEGTS_STREAM_TYPE_ISO13818_V)
			{
				int i;
				for(i=0; i < frame_sz-4; i++)
				{
					if (stream_packet->stream_buffer.buffer[i] == 0x00 && stream_packet->stream_buffer.buffer[i+1] == 0x00 &&
						stream_packet->stream_buffer.buffer[i+2] == 0x01 && stream_packet->stream_buffer.buffer[i+3] == 0x00)
					{
						uint8_t frame_type = (stream_packet->stream_buffer.buffer[i+5] & 0x38) >> 3;
						if (frame_type == 1)
						{
							m_WaitForVideoFrame = 0;
							XBMC->Log(LOG_DEBUG, "[EtpProcessStream] MPEG2 PIC HEADER: %d -- I_FRAME\n", frame_type);
							break;
						}
					}
				}

				if (i >= frame_sz - 4)
				{
//					XBMC->Log(LOG_DEBUG, "no i-frame\n");
					stream_packet->stream_buffer.buffer_pos = 0;
					return -10;
				}

			}
		}

		dPacket = PVR->AllocateDemuxPacket(frame_sz);
		dPacket->duration = stream_packet->map->duration;
		dPacket->iSize = frame_sz;
		dPacket->iStreamId = PidGetIndex(stream_packet->pid);

		if (!pts)
		{
			dPacket->dts = dPacket->pts = DVD_NOPTS_VALUE;
		}
		else
		{
			dPacket->dts = dPacket->pts = PTS_to_DVD(pts - m_StartTime);
			if (dts)
				dPacket->dts = PTS_to_DVD(dts - m_StartTime);
		}

		memcpy(dPacket->pData, stream_packet->stream_buffer.buffer + frame_start, dPacket->iSize);

		stream_packet->frame_count++;

		if (m_Props.iStreamCount && m_StreamProcessCount < m_Props.iStreamCount)
		{
			int i;
			for(i=0; i< ETP_MAX_PID_COUNT; i++)
				if (m_StreamFoundMsg[i] == stream_packet->pid)
					break;

			if (i >= ETP_MAX_PID_COUNT)
			{
				XBMC->Log(LOG_DEBUG, "[EtpProcessStream]\tPushing PID: 0x%x (%d) -- PTS: %" PRId64" (%0.2f), DTS: %" PRId64" (%0.2f)\n", stream_packet->pid, stream_packet->pid, pts, pts/90000.0, dts, dts/90000.0);
				for(i=0; i < ETP_MAX_PID_COUNT; i++)
				{
					if (m_StreamFoundMsg[i] == 0)
					{
						m_StreamFoundMsg[i] = stream_packet->pid;
						break;
					}
				}
			}
		}

		while (StreamAlive() && m_demuxPacketBuffer->Push(dPacket) == false)
		{
//			XBMC->Log(LOG_DEBUG, "Read waiting for demux buffer: %" PRId64" (seek: %d)\n", m_demuxPacketBuffer->Size(), m_Seeking);
			usleep(32*1000);
			if (m_Seeking)
			{
				PVR->FreeDemuxPacket(dPacket);
				return -8;
			}

		}
/*
		if (stream_changed)
		{
			XBMC->Log(LOG_DEBUG, "Sending DMX_SPECIALID_STREAMCHANGE\n");
			dPacket = PVR->AllocateDemuxPacket(0);
			dPacket->iStreamId = DMX_SPECIALID_STREAMCHANGE;
			m_demuxPacketBuffer->Push(dPacket);
		}
*/

		stream_packet->stream_buffer.buffer_pos -= (size_t)frame_end;
		if (stream_packet->stream_buffer.buffer_pos)
			memmove(stream_packet->stream_buffer.buffer, stream_packet->stream_buffer.buffer + frame_end, stream_packet->stream_buffer.buffer_pos);

		return frame_sz;
	}
	return -4;
}

ETP_STREAM_PACKET *PVREtp::EtpStreamInit(uint16_t pid)
{
	ETP_STREAM_PACKET * stream_packet = NULL;

	if (pid && PidIsValid(pid))
	{
		ETP_PID_MAP *map = PidGetMap(pid);

		if (!map)
		{
			XBMC->Log(LOG_DEBUG, "[EtpStreamInit] BAD! No map for PID: 0x%x\n", pid);
			return NULL;
		}

		stream_packet = EtpAddStream(pid);

		if (!stream_packet)
		{
//			XBMC->Log(LOG_DEBUG, "[0x%x] stream_packet, bad\n", pid);
			return NULL;
		}

		stream_packet->map = map;

		if (!stream_packet->stream_buffer.buffer)
		{
			if (map->type == MPEGTS_TYPE_VIDEO)
				stream_packet->stream_buffer.alloc_size = VIDEO_FRAME_SIZE_INIT;
			else
				stream_packet->stream_buffer.alloc_size = AUDIO_FRAME_SIZE_INIT;

			if (stream_packet->stream_buffer.buffer)
				free(stream_packet->stream_buffer.buffer);

//			XBMC->Log(LOG_DEBUG, "[EtpStreamInit] [PID: 0x%x] New Mem (%" PRId64")\n", stream_packet->pid, stream_packet->stream_buffer.alloc_size);
			stream_packet->stream_buffer.buffer = (uint8_t*)calloc(1, stream_packet->stream_buffer.alloc_size);
		}
		else
		{
			XBMC->Log(LOG_DEBUG, "[EtpStreamInit] [PID: 0x%x] re-use old mem (%" PRId64")\n", stream_packet->pid, stream_packet->stream_buffer.alloc_size);
		}

		stream_packet->stream_buffer.buffer_pos = 0;
		stream_packet->has_start = 0;
		stream_packet->need_sync = 0;
	}
	return stream_packet;
}

void *PVREtp::UpdateInfo(void)
{
	uint64_t seq = 0;
	time_t now=0, lastTriggerUpd = 0, last_alive = 0, lastBufTimeUpd = 0;
	m_UpdateThreadOpts.inited = true;

	while(m_demuxPacketBuffer && m_ETPProto)
	{
		now = TimeNow();

		if (m_ForceUpdateInfo || (now - lastTriggerUpd > ETP_UPDATE_INFO_INTERVAL))
		{
			if (m_ForceUpdateInfo)
				m_ForceUpdateInfo = false;

			seq = m_ETPProto->GetRecordingSeqNum();
//			XBMC->Log(LOG_DEBUG, "RecSEQ: %" PRId64", cur: %" PRId64"\n", seq, m_RecSeqNum);
			if ((int64_t)seq == -1)
			{
//				XBMC->Log(LOG_DEBUG, "BAD1\n");
				usleep(1000 * 1000);
				m_RecSeqNum = -1;
				continue;
			}

			if (m_RecSeqNum != seq)
			{
				m_RecTriggered = 0;
//				XBMC->Log(LOG_DEBUG, "TriggerRecUP\n");
				PVR->TriggerRecordingUpdate();

				while(m_RecTriggered == 0)
				{
					usleep(500*1000);
				}
			}

			seq = m_ETPProto->GetTimerSeqNum();
			if (m_TimerSeqNum != seq)
			{
				m_TimerTriggered = 0;
				PVR->TriggerTimerUpdate();

				while(m_TimerTriggered == 0)
				{
					usleep(500*1000);
				}
			}
			lastTriggerUpd = now;
		}

		if (now - lastBufTimeUpd > 2)
		{
			if (m_StartTime && m_StreamProcessCount && m_Props.iStreamCount)
			{
				m_CurrentTime = m_ETPProto->StreamGetTimePos();
				m_EndTime = m_ETPProto->StreamGetTimeEnd();
			}
			lastBufTimeUpd = now;
		}

		if (StreamAlive() && m_Paused)
		{
//			XBMC->Log(LOG_DEBUG, "[UpdateInfo] Paused\n");
			DemuxPacket *dp = PVR->AllocateDemuxPacket(0);
			m_demuxPacketBuffer->Push(dp);
			if (now - last_alive > 2)
			{
				m_ETPProto->SendKeepAlive();
				last_alive = now;
			}
		}
		else
			last_alive = 0;

		usleep(1000 * 500);
	}
	XBMC->Log(LOG_DEBUG, "[UpdateInfo] Exit\n");
	m_UpdateThreadOpts.tid = 0;
	return NULL;
}

void *PVREtp::ProcessFrames(void)
{
//	time_t time_last = 0, time_now = 0;
	uint8_t *_TsBuffer = NULL;
	DemuxPacket *dp = NULL;

	if (m_FramesThreadOpts.inited == true)
	{
		XBMC->Log(LOG_DEBUG, "[ProcessFrames] Thread Already Running\n");
		return NULL;
	}

	m_FramesThreadOpts.inited = true;
	XBMC->Log(LOG_DEBUG, "[ProcessFrames] Started Thread\n");

	while (m_ReadThreadOpts.inited)
	{
		while (m_Props.iStreamCount == 0)
		{
//			XBMC->Log(LOG_DEBUG, "[ProcessFrames] Waiting for get Stream Info\n");
			usleep(1000*1000);
		}

		while (StreamAlive() && m_Seeking)
		{
			while (m_demuxPacketBuffer->Pop(dp))
				PVR->FreeDemuxPacket(dp);

			m_SeekedProcessACK = 1;
			usleep(1000*32);

		}

		if (StreamAlive() && !m_StreamDie && m_NetworkBuffer->Pop(_TsBuffer, 128))
		{
			ETP_PID_MAP *map = NULL;
			MPEGTS_PAYLOAD_START *start = NULL;
			BITSTREAM *bitstream = NULL;
			MPEGTS_PACKET * packet = NULL;
			ETP_STREAM_PACKET *stream_packet = NULL;
			uint8_t offset = 0;
			uint16_t pid = 0;
			uint32_t i;
//			uint64_t *bcount  = (uint64_t*)&_TsBuffer[m_TsBufferSize+1];
//			XBMC->Log(LOG_DEBUG, "ProcessFrames: %" PRId64"\n", *bcount);
			for(i=0; i < m_TsBufferSize; i += TS_PACKET_SIZE)
			{
				offset = 0;
				pid = 0;
				packet = NULL;
				bitstream = NULL;
				start  = NULL;
				map = NULL;

				if (m_Seeking)
				{
					XBMC->Log(LOG_DEBUG, "[ProcessFrames] ------ Breaking out of process loop\n");
					break;
				}

				if (_TsBuffer[i] == TS_SYNC_BYTE)
				{
					map = NULL;
					start = NULL;
					bitstream = NULL;
					packet = NULL;
					pid = ((_TsBuffer[i+1] & 0x1F) << 8) | _TsBuffer[i+2];


					if (m_StreamIsRecording && pid == NULL_PID && m_PacketCount > 10)
					{
						int q = 0;
						for (q=0; q < TS_PACKET_SIZE; q++)
							if( _TsBuffer[i+q] != SPECIAL_NULL_PACKET[q])
								break;

						if (q >= TS_PACKET_SIZE)
						{
							m_NullPacketCount++;
//							XBMC->Log(LOG_DEBUG, "NullCount: %" PRId64"\n", m_NullPacketCount);
						}
					}
					if (pid > 0 && pid != NULL_PID)
						m_PacketCount++;

					if (pid == NULL_PID || !pid || !PidIsValid(pid))
					{
						if (pid == NULL_PID)
						{
							int q = 0;
//							XBMC->Log(LOG_DEBUG, "NULL PID\n");

							for (q=0; q < TS_PACKET_SIZE - 4; q++)
							{
								if( _TsBuffer[i+q] != SPECIAL_NULL_PACKET_SIGNAL_INFO[q])
									break;
							}

							if (q >= TS_PACKET_SIZE - 4)
							{
								memcpy(&m_SignalStrength, &_TsBuffer[i + TS_PACKET_SIZE - 4], sizeof(m_SignalStrength));
								memcpy(&m_SignalNoiseRatio, &_TsBuffer[i + TS_PACKET_SIZE - 2], sizeof(m_SignalNoiseRatio));
								//XBMC->Log(LOG_DEBUG, "HAZ SIGNAL NULL PID: %d %d\n", m_SignalStrength, m_SignalNoiseRatio);
							}
						}
//						XBMC->Log(LOG_DEBUG, "Invalid PID: 0x%x\n", pid);
						continue;
					}

					bitstream = bits_create(&_TsBuffer[i], TS_PACKET_SIZE);
					if (bitstream)
					{
						packet = mpegts_parse_packet2(bitstream, 0);

						if (packet && packet->payload_start && packet->payload_start_struct)
						{
							start = packet->payload_start_struct;
							// Fix 33bit roll-over
//							XBMC->Log(LOG_DEBUG, "PTS1: %0.2f, DTS: %0.2f (%" PRId64")\n", (start->pts/90000.0) -(m_StartTime/90000.0) , start->dts/90000.0, m_StartTime);

							if (m_StartTime)
							{
								if (start->pts && m_StartTime > start->pts)
								{
									if (m_StartTime - start->pts >= 90000 * 2)
									{
//										XBMC->Log(LOG_DEBUG, "Roll1\n");
										start->pts += MAX_33BITS;
									}
								}

								if (start->dts && m_StartTime > start->dts)
								{
									if (m_StartTime - start->dts >= 90000 * 2)
									{
//										XBMC->Log(LOG_DEBUG, "Roll2, start: %" PRId64", dts: %" PRId64"\n", m_StartTime, start->dts);
										start->dts += MAX_33BITS;
									}
								}
							}
//							XBMC->Log(LOG_DEBUG, "\tPTS2: %" PRId64", DTS: %" PRId64"\n", start->pts, start->dts);
						}
						offset = bitstream->bit_offset/8;
						free(bitstream);
					}
					else
					{
						XBMC->Log(LOG_DEBUG, "[ProcessFrames] bistream null, PID: 0x%x\n", pid);
						if (packet)
							mpegts_free_packet(packet);

						continue;
					}

					if (m_StreamIsRecording && start && !m_StartTime)
					{
						if (start->pts > 45000)
							m_StartTime = (start->pts - 45000);
						else
							m_StartTime = start->pts;
					}

					map = PidGetMap(pid);

					if (!map)
					{
						XBMC->Log(LOG_DEBUG, "[ProcessFrames] BAD! No map for PID: 0x%x\n", pid);
						if (packet)
							mpegts_free_packet(packet);

						continue;
					}

					stream_packet = EtpGetStream(pid);

					if (!stream_packet && map)
						stream_packet = EtpStreamInit(pid);

					if (!stream_packet || offset < 4 || offset > TS_PACKET_SIZE)
					{
//						XBMC->Log(LOG_DEBUG, "stream_packet or offset is bad, strm: %d -- off: %d\n", !!stream_packet, offset); 
						if (packet)
							mpegts_free_packet(packet);
						continue;
					}
					stream_packet->map = map;

					if (start)
					{
						if (m_StartTime)
						{
							if (start->pts < m_StartTime || (start->dts && start->dts < m_StartTime))
							{
								if (packet)
									mpegts_free_packet(packet);

								stream_packet->has_start = 0;
								stream_packet->stream_buffer.buffer_pos = 0;

//								XBMC->Log(LOG_DEBUG, "[ProcessFrames] -!!-- PTS BAD: 0x%x\n", pid);
								continue;
							}
						}

						if(stream_packet->has_start)
						{
							if ((map->frame_size == 0) || (map->frame_size && stream_packet->stream_buffer.buffer_pos >= (size_t)map->frame_size))
							{
								EtpProcessStream(stream_packet);
							}
						}

						if (map->type == MPEGTS_TYPE_AUDIO)
						{
							if (start->pts)
							{
								map->first_pts = start->pts;
								stream_packet->frame_count = 0;
							}
						}

						stream_packet->has_start = 1;
						stream_packet->stream_buffer.timing.pts = start->pts;
						stream_packet->stream_buffer.timing.dts = start->dts;
						stream_packet->stream_buffer.timing.duration = map->duration;
					}

					if(stream_packet->has_start)
					{
						if (start)
						{
							uint8_t reset = 1;
							if (stream_packet->map->frame_size)
							{
								if (stream_packet->stream_buffer.buffer_pos < (uint64_t)stream_packet->map->frame_size)
								{
									reset = 0;
								}
							}

							if (reset)
							{
								stream_packet->stream_buffer.buffer_pos = 0;
								memset(stream_packet->stream_buffer.buffer, 0, stream_packet->stream_buffer.alloc_size);
							}
						}

						EtpAddStreamData(pid, &_TsBuffer[i+offset], (int)(TS_PACKET_SIZE - offset));

						if(map->frame_size &&
							stream_packet->stream_buffer.buffer_pos >= (size_t)map->frame_size &&
							map->type == MPEGTS_TYPE_AUDIO)
						{
							int r = 0;
							do
							{
								r = EtpProcessStream(stream_packet);
								if (r < 0)
									break;
								if (map->type == MPEGTS_TYPE_AUDIO)
								{
									if (stream_packet->stream_buffer.buffer_pos >= (size_t)map->frame_size)
										XBMC->Log(LOG_DEBUG, "[ProcessFrames]\tPID: 0x%x, Audio Buf Size: %" PRId64" (frsz: %d) -- 0x%02x 0x%02x\n", stream_packet->pid, stream_packet->stream_buffer.buffer_pos, map->frame_size,  stream_packet->stream_buffer.buffer[0],  stream_packet->stream_buffer.buffer[1]);
								}
							} while(StreamAlive() && stream_packet->stream_buffer.buffer_pos >= (size_t)map->frame_size);
						}

					}
				}
				else
					XBMC->Log(LOG_DEBUG, "[ProcessFrames] Missing sync byte! %d\n", i);

				if (packet)
					mpegts_free_packet(packet);
			}

			if (!m_UseMemList || m_MemList->Push(_TsBuffer) == false)
				free(_TsBuffer);
		}
/*
		else
		{
			if (!m_StreamDie)
				usleep(32 * 1000);
		}
*/
	}

	while (m_demuxPacketBuffer->Pop(dp))
		PVR->FreeDemuxPacket(dp);

	XBMC->Log(LOG_DEBUG, "[ProcessFrame] Thread Exiting\n");
	m_FramesThreadOpts.inited = false;
	return NULL;
}

void *PVREtp::ProcessRead(void)
{
	uint8_t *_TsBuffer = NULL;
	uint32_t ret=0;
	uint32_t bad_count = 0;
//	time_t last_keep_alive = 0, time_now = 0;
	time_t bad_start = 0, bad_now = 0;

	if (m_ReadThreadOpts.inited == true)
	{
		XBMC->Log(LOG_DEBUG, "[ProcessRead] Thread Already Running\n");
		return NULL;
	}
	m_ReadThreadOpts.inited = true;

	XBMC->Log(LOG_DEBUG, "[ProcessRead] Started Thread\n");
	while (!StreamAlive())
	{
//		XBMC->Log(LOG_DEBUG, "P1 Waiting...\n");
		usleep(1000*1000);
	}

	XBMC->Log(LOG_DEBUG, "[ProcessRead] Buf Size: %d\n", m_TsBufferSize);

	while (StreamAlive() && !m_StreamDie)
	{
	//	XBMC->Log(LOG_DEBUG, "[ProcessRead] Loop Started: %d %d\n", StreamAlive(), !m_StreamDie);
		if (m_Paused)
		{
//			XBMC->Log(LOG_DEBUG, "[ProcessRead] Paused\n");
			usleep(1000*1000);
			continue;
		}

		while (m_Seeking && StreamAlive())
		{
//			XBMC->Log(LOG_DEBUG, "[PLUG] is Seeking, waiting from read\n");
			while (m_NetworkBuffer->Pop(_TsBuffer))
			{
				if (!m_UseMemList || m_MemList->Push(_TsBuffer) == false)
					free(_TsBuffer);
			}

			m_SeekedReadACK = 1;
			usleep(1000*128);
		}

		if (!m_Paused)
		{
			if (m_MemList->Size() == 0 || m_MemList->Pop(_TsBuffer) == false)
				_TsBuffer = (uint8_t *)malloc(m_TsBufferSize);
		}
		else
		{
			_TsBuffer = NULL;
		}

		if (_TsBuffer && !m_StreamDie)
		{
			ret = ReadLiveStream((unsigned char*)_TsBuffer, m_TsBufferSize);

			if (ret > 0 && (ret % TS_PACKET_SIZE) == 0)
			{
				bad_start = bad_now = 0;
				bad_count = 0;
				while(StreamAlive() && m_NetworkBuffer->Push(_TsBuffer) == false)
				{
//					XBMC->Log(LOG_DEBUG, "Read waiting for network buffer: %" PRId64"\n",m_NetworkBuffer->Size());
					usleep(32*1000);
					if (m_Seeking)
					{
						if (_TsBuffer)
						{
							if (!m_UseMemList || m_MemList->Push(_TsBuffer) == false)
								free(_TsBuffer);
						}
						break;
					}
				}
/*
				if (ret < m_TsBufferSize)
				{
					XBMC->Log(LOG_DEBUG, "[ProcessRead] BBBBBBBBBBLEAH\n");
				}
*/
			}
			else
			{
				if (_TsBuffer)
				{
					if (!m_UseMemList || m_MemList->Push(_TsBuffer) == false)
						free(_TsBuffer);
				}

				if (bad_count > 5)
				{
//					XBMC->Log(LOG_DEBUG, "[ProcessRead] BLAGSGHIQ: %d\n", errno);
					m_StreamDie = 1;
					continue;
				}

				bad_count++;
				if (errno == -1)
				{
					usleep(64*1000);
					XBMC->Log(LOG_DEBUG, "[ProcessRead] ReadLive bad: %d %d (%s) (%d), count: %d\n", ret, m_TsBufferSize, strerror(errno), errno, bad_count);
					if ((int)ret < 0)
					{
						XBMC->Log(LOG_DEBUG, "[ProcessRead] EXIT1\n");
						m_StreamDie = 1;
						continue;
					}
				}
				else if (errno == 0)
				{
					bad_now = TimeNow();
					if (!bad_start)
					{
						bad_start = bad_now;
					}
					else
					{
						if (bad_now - bad_start > 3)
						{
							XBMC->Log(LOG_DEBUG, "[ProcessRead] Bad Exiting\n");
							m_StreamDie = 1;
						}
					}
					XBMC->Log(LOG_DEBUG, "[ProcessRead] ReadLive bad, sleep: %d (%s), count: %d\n", errno,strerror(errno), bad_count);
					usleep(1000 *100);
				}
				else
				{
					XBMC->Log(LOG_DEBUG, "[ProcessRead] No errno: %d\n", errno);
				}
			}
		}
		else
		{
			XBMC->Log(LOG_DEBUG, "_tsbuffer null\n");
			if (!m_Paused)
				XBMC->Log(LOG_DEBUG, "[ProcessRead] MALLOC BAD\n");
			else
				usleep(100*1000);
		}
	}

	m_ReadThreadOpts.inited = false;
	while(m_FramesThreadOpts.inited)
	{
		XBMC->Log(LOG_DEBUG, "[ProcessRead] Waiting for ProcessFrames to die\n");
		usleep(250*1000);
	}

	EtpResetStream();
	PidsClear();
	DemuxFlush();

	XBMC->Log(LOG_DEBUG, "[ProcessRead] ReadThread Exiting!\n");

	return NULL;
}

ETP_PID_MAP * PVREtp::PidGetMap(uint16_t pid)
{
	int j = PidGetIndex(pid);
	if (j >= 0)
		return &m_PidMap[j];
	return NULL;
}

int PVREtp::PidGetIndex(uint16_t pid)
{
	int j;
	for(j = 0; j < m_PidMapCount; j++)
		if(m_PidMap[j].pid == pid)
			return j;
	return -1;
}

ETP_PID_MAP * PVREtp::PidAdd(uint16_t pid)
{
	m_PidMap[m_PidMapCount++].pid = pid;
	return &m_PidMap[m_PidMapCount - 1];
}

int	PVREtp::PidIsValid(uint16_t pid)
{
	int j;
	for(j = 0; j < m_PidMapCount; j++)
		if(m_PidMap[j].pid == pid)
			return 1;
	return 0;
}

void PVREtp::PidsClear(void)
{
	int i;
	for(i=0; i < PVR_STREAM_MAX_STREAMS; i++)
	{
		m_PidMap[i].pid = 0;
	}
	m_PidMapCount = 0;
}

bool PVREtp::StreamAlive(void)
{
	return (m_ETPProto && m_ETPProto->IsStreaming());
}

void *PVREtp::thread_func(void*p)
{
	THREAD_OPTS *opts = (THREAD_OPTS*)p;
	if (!opts)
	{
		XBMC->Log(LOG_DEBUG, "[thread_func] error 1\n");
		return NULL;
	}

	PVREtp *pvr = (PVREtp *)opts->_this;
	if (pvr)
	{
		switch(opts->thread_type)
		{
			case ETP_THREAD_READ:
				return pvr->ProcessRead();
			break;

			case ETP_THREAD_PROCESS:
				return pvr->ProcessFrames();
			break;

			case ETP_THREAD_UPDATES:
				return pvr->UpdateInfo();
			break;
		}
	}
	return NULL;
}

bool PVREtp::StreamOpenNext(void)
{
	if (m_ETPProto)
	{
		XBMC->Log(LOG_DEBUG, "[StreamOpenNext] Start, IP [%s]\n", (char*)m_strRemoteIP.c_str());
		if (m_ETPProto->TuneGetCtrlPort() == true)
		{
			int i;
			uint8_t *_TsBuffer = NULL;
			XBMC->Log(LOG_DEBUG, "[StreamOpenNext] Start, Port [%d]\n", m_usRemoteDataPort);

			DemuxPacket *dp = NULL;
			m_BufTimeOffset = 0;
			m_StartTime = 0;
			m_StreamDie = 0;
			m_LastEndTimeUpdate = 0;
			m_EtpStreamCount = 0;
			m_StreamOpenWaitTime = 3;
			m_WaitForVideoFrame = 1;
			m_Paused = false;
			m_NullPacketCount = 0;

			m_CurrentTime = 0;
			m_EndTime = m_StartTime = 0;
			m_PidMapCount = 0;
			m_PacketCount = 0;
			m_Seeking = 0;
			m_VPushed = 0;
			m_APushed = 0;

			XBMC->Log(LOG_DEBUG, "[StreamOpenNext] 1\n");
			while (m_demuxPacketBuffer->Pop(dp))
				PVR->FreeDemuxPacket(dp);

			XBMC->Log(LOG_DEBUG, "[StreamOpenNext] 2\n");
			while (m_NetworkBuffer->Pop(_TsBuffer))
			{
				if (!m_UseMemList || m_MemList->Push(_TsBuffer) == false)
					free(_TsBuffer);
			}
			XBMC->Log(LOG_DEBUG, "[StreamOpenNext] 3\n");

			m_StreamProcessCount = 0;
			for(i=0; i < ETP_MAX_PID_COUNT; i++)
			{
				m_StreamProcessMsg[i] = m_StreamFoundMsg[i] = 0;
			}
			XBMC->Log(LOG_DEBUG, "[StreamOpenNext] 4\n");

			memset(&m_Props, 0, sizeof(m_Props));
			PidsClear();
			EtpResetStream();

			if (m_bDoDemux)
			{
				m_LastSeekTime = 0;
				m_ReadThreadOpts.inited = 0;
				m_FramesThreadOpts.inited = 0;
			XBMC->Log(LOG_DEBUG, "[StreamOpenNext] 5\n");

				pthread_create(&m_ReadThreadOpts.tid, NULL, &thread_func, (void*)&m_ReadThreadOpts);
				while(m_ReadThreadOpts.inited == 0)
				{
					usleep(1000*300);
//					XBMC->Log(LOG_DEBUG, "Waiting for readthread\n");
				}
			XBMC->Log(LOG_DEBUG, "[StreamOpenNext] 6\n");

				pthread_create(&m_FramesThreadOpts.tid, NULL, &thread_func, (void*)&m_FramesThreadOpts);
				while(m_FramesThreadOpts.inited == 0)
				{
					usleep(1000*300);
	//				XBMC->Log(LOG_DEBUG, "Waiting for processthread\n");
				}
			XBMC->Log(LOG_DEBUG, "[StreamOpenNext] 7\n");

				m_StreamOpenTime = TimeNow();
				XBMC->Log(LOG_DEBUG, "[StreamOpenNext] Demux Open Good\n");
			}
			return true;
		}
		else
			XBMC->Log(LOG_DEBUG, "m_ETPProto->TuneGetCtrlPort was bad!!\n");
	
	}
	return false;
}

bool PVREtp::OpenRecordedStream(const PVR_RECORDING &recording)
{
	bool ret = false;
	int rec_id = -1;

	if (strstr(recording.strStreamURL, PVR_URL_HEADER) != NULL)
	{
		char *tmp = (char *)&recording.strStreamURL[strlen(PVR_URL_HEADER)];
		rec_id = atoi(tmp);
	}

	if (rec_id == -1)
	{
		XBMC->Log(LOG_DEBUG, "[OpenRecordedStream] BLAH\n");
		return false;
	}

	while (m_FramesThreadOpts.inited || m_ReadThreadOpts.inited)
	{
//		XBMC->Log(LOG_DEBUG, "[OpenRecordedStream] Need to wait m_FramesThreadOpts.inited: %d, m_ReadThreadOpts.inited: %d\n", m_FramesThreadOpts.inited, m_ReadThreadOpts.inited);
		usleep(1000*1000);
	}

	XBMC->Log(LOG_DEBUG, "[OpenRecordedStream] Play ID: %d, m_PidMapCount: %d, m_EtpStreamCount: %d\n", rec_id, m_PidMapCount, m_EtpStreamCount);
	if (m_ETPProto)
	{
		if (m_ETPProto->TuneRecording(rec_id) == true)
		{
			m_StreamIsRecording = true;

			if (StreamOpenNext())
			{
				if (GetStartTime() == ETP_INVALID_TIME)
				{
					XBMC->Log(LOG_DEBUG, "[OpenRecordedStream] start time is invalid?\n");
					CloseRecordedStream();
					return false;
				}
				return true;
			}
		}
	}
	XBMC->Log(LOG_DEBUG, "[OpenRecordedStream] RET: %d\n", ret);
	return ret;
}

void PVREtp::CloseRecordedStream(void)
{
	XBMC->Log(LOG_DEBUG, "[CloseRecordedStream]\n");
	CloseLiveStream();
	DemuxFlush();
}

int PVREtp::ReadRecordedStream(unsigned char *pBuffer, unsigned int iBufferSize)
{
	return ReadLiveStream(pBuffer, iBufferSize);
}

long long PVREtp::SeekRecordedStream(long long iPosition, int iWhence /* = SEEK_SET */)
{
	XBMC->Log(LOG_DEBUG, "[SeekRecordedStream] iPos: %lld, iWhence: %d\n", iPosition, iWhence);
	return -1;
}

long long PVREtp::PositionRecordedStream(void)
{
	XBMC->Log(LOG_DEBUG, "[PositionRecordedStream]\n");
	return -1;
}

long long PVREtp::LengthRecordedStream(void)
{
	long long ret = -1;
	if (!m_EndTime)
	{
		if (m_ETPProto)
			m_EndTime = m_ETPProto->StreamGetTimeEnd();
	}

	if ((int64_t) m_EndTime != -1 && m_EndTime > m_StartTime)
		ret = (long long)(m_EndTime - m_StartTime);

	return ret;
}

PVR_ERROR PVREtp::DeleteRecording(const PVR_RECORDING &recording)
{
	int rec_id = -1;

	if (strstr(recording.strStreamURL, PVR_URL_HEADER) != NULL)
	{
		char *tmp = (char *)&recording.strStreamURL[strlen(PVR_URL_HEADER)];
		rec_id = atoi(tmp);
	}

	if (rec_id == -1)
	{
		XBMC->Log(LOG_DEBUG, "BLAH\n");
		return PVR_ERROR_FAILED;
	}

	XBMC->Log(LOG_DEBUG, "PVREtp::DeleteRecordedStream: %d\n", rec_id);

	if (m_ETPProto)
	{
		if (m_ETPProto->DeleteRecording((uint32_t)rec_id) == true)
		{
			m_ForceUpdateInfo = true;
//			PVR->TriggerRecordingUpdate();
			return PVR_ERROR_NO_ERROR;
		}
		else
		{
			XBMC->Log(LOG_DEBUG, "PVREtp::DelteRec BAD\n");
			return PVR_ERROR_FAILED;
		}
	}
	return PVR_ERROR_SERVER_TIMEOUT;
}

int PVREtp::GetTimersAmount(void)
{
	m_TimerCount = 0;
	if (m_ETPProto)
		m_TimerCount = m_ETPProto->GetTimerCount();

	return m_TimerCount;
}

PVR_ERROR PVREtp::GetTimers(ADDON_HANDLE handle)
{
	PVR_ERROR ret = PVR_ERROR_SERVER_TIMEOUT;
	PVR_TIMER *tmp = NULL;
	PLATFORM::CLockObject lock(m_TimerUpdateLock);

	m_TimerCount = m_ETPProto->GetTimerCount();
	if (m_TimerCount == 0)
	{
		XBMC->Log(LOG_DEBUG, "[GetTimers] no timers in system\n");

		m_TimerSeqNum = m_ETPProto->GetTimerSeqNum();
		while (m_TimerCache->Pop(tmp))
			delete tmp;

		m_TimerTriggered = 1;
		return PVR_ERROR_NO_ERROR;
	}

	if (m_TimerCache && m_TimerCache->Size())
	{
//		XBMC->Log(LOG_DEBUG, "GetTimers1\n");
		if (m_ETPProto->GetTimerSeqNum() == m_TimerSeqNum)
		{
			tmp = new PVR_TIMER();
			tmp->iClientIndex = 99;
			tmp->iClientChannelUid = -1;
			m_TimerCache->Push(tmp);
			while (m_TimerCache->Pop(tmp))
			{
//				XBMC->Log(LOG_DEBUG, "GetTimers cache: %d %d\n", tmp->iClientIndex,tmp->iClientChannelUid );
				if (tmp->iClientIndex == 99 && tmp->iClientChannelUid == -1)
				{
					m_TimerTriggered = 1;
					XBMC->Log(LOG_DEBUG, "[GetTimers] sending back cache\n");
			 		return PVR_ERROR_NO_ERROR;
				}
				m_TimerCache->Push(tmp);
				PVR->TransferTimerEntry(handle, tmp);
			}
		}
		else
		{
//			XBMC->Log(LOG_DEBUG, "[GetTimers] emptying cache\n");
			while (m_TimerCache->Pop(tmp))
				delete tmp;
		}
	}


	if (m_ETPProto && handle)
	{
		uint32_t count = m_ETPProto->GetTimerList();
//		XBMC->Log(LOG_DEBUG, "GetTimers2: %d\n", count);

		if (count)
		{
			PVR_TIMER xbmcTimer;
			TIMER_STRUCT timer = {0};
			uint32_t _count = 0;

			while(_count < count)
			{
				memset(&xbmcTimer, 0, sizeof(xbmcTimer));

				if (m_ETPProto->GetTimerListNext(&timer.data) == true)
				{
					PVR_TIMER *cache = new PVR_TIMER();
/*
				XBMC->Log(LOG_DEBUG, "[%" PRId64"],[%d],[%" PRId64"],[%" PRId64"],[%s],[%s],[%s],[%s],[%s],[%s]\n", 
timer.data.channel_num, timer.data.id, timer.data.start_epoch, timer.data.end_epoch, 
timer.channel_name, timer.data.title, timer.data.plot_outline, timer.data.plot, timer.data.genre, timer.data.sub_genre);
*/
					xbmcTimer.iClientIndex = timer.data.id;
					xbmcTimer.iClientChannelUid = (int)timer.data.channel_num;
					xbmcTimer.startTime = timer.data.start_epoch;
					xbmcTimer.endTime = timer.data.end_epoch;
					snprintf(xbmcTimer.strTitle, PVR_ADDON_NAME_STRING_LENGTH -1, "%s", timer.data.title);
					snprintf(xbmcTimer.strSummary, PVR_ADDON_NAME_STRING_LENGTH -1, "%s", timer.data.plot);
					xbmcTimer.iGenreType = GetGenreFromStr(timer.data.genre, timer.data.sub_genre);

					xbmcTimer.state = PVR_TIMER_STATE_SCHEDULED;
					xbmcTimer.state = PVR_TIMER_STATE_SCHEDULED;
					memcpy(cache, &xbmcTimer, sizeof(PVR_TIMER));
					if (m_TimerCache)
						m_TimerCache->Push(cache);

					PVR->TransferTimerEntry(handle, &xbmcTimer);
					_count++;
				}
			}

			m_TimerSeqNum = m_ETPProto->GetTimerSeqNum();

			if (_count == count )
			 	ret = PVR_ERROR_NO_ERROR;
			else
				ret = PVR_ERROR_FAILED;
		}

		m_ETPProto->GetTimerListDone();

	}
	m_TimerTriggered = 1;
	return ret;
}

PVR_ERROR PVREtp::AddTimer(const PVR_TIMER &timer)
{
	TIMER_STRUCT etimer = {0};

	etimer.data.start_epoch = timer.startTime;
	etimer.data.end_epoch = timer.endTime;
	etimer.data.channel_num = timer.iClientChannelUid;

	XBMC->Log(LOG_DEBUG, "PVREtp::AddTimer\n");
	XBMC->Log(LOG_DEBUG, "\tiClientIndex: %d\n", timer.iClientIndex);
	XBMC->Log(LOG_DEBUG, "\tiClientChannelUid: %d\n", timer.iClientChannelUid);
//	XBMC->Log(LOG_DEBUG, "\tstartTime: %" PRId64" (%s)\n", timer.startTime, xmltv_epoch_to_str(timer.startTime));
//	XBMC->Log(LOG_DEBUG, "\tendTime: %" PRId64" (%s)\n", timer.endTime, xmltv_epoch_to_str(timer.startTime));
	XBMC->Log(LOG_DEBUG, "\tstate: %d\n", timer.state);
	XBMC->Log(LOG_DEBUG, "\ttitle: %s\n", timer.strTitle);
/*
	XBMC->Log(LOG_DEBUG, "\tstrDirectory: %s\n", timer.strDirectory);
	XBMC->Log(LOG_DEBUG, "\tstrSummary: %s\n", timer.strSummary);
	XBMC->Log(LOG_DEBUG, "\tiPriority: %d\n", timer.iPriority);
	XBMC->Log(LOG_DEBUG, "\tiLifetime: %d\n", timer.iLifetime);
	XBMC->Log(LOG_DEBUG, "\tbIsRepeating: %d\n", timer.bIsRepeating);
	XBMC->Log(LOG_DEBUG, "\tfirstDay: %" PRId64"\n", timer.firstDay);
	XBMC->Log(LOG_DEBUG, "\tiWeekdays: %d\n", timer.iWeekdays);
	XBMC->Log(LOG_DEBUG, "\tiEpgUid: %d\n", timer.iEpgUid);
	XBMC->Log(LOG_DEBUG, "\tiMarginStart: %d\n", timer.iMarginStart);
	XBMC->Log(LOG_DEBUG, "\tiMarginEnd: %d\n", timer.iMarginEnd);
	XBMC->Log(LOG_DEBUG, "\tiGenreType: %d\n", timer.iGenreType);
	XBMC->Log(LOG_DEBUG, "\tiGenreSubType: %d\n", timer.iGenreSubType);
*/

	if (m_ETPProto && m_ETPProto->AddTimer(&etimer.data) == true)
	{
		m_ForceUpdateInfo = true;
		return PVR_ERROR_NO_ERROR;
		//PVR->TriggerTimerUpdate();
	}
	else
	{
		XBMC->Log(LOG_DEBUG, "PVREtp::AddTimer BAD\n");
		return PVR_ERROR_FAILED;
	}
	return PVR_ERROR_SERVER_TIMEOUT;
}

PVR_ERROR PVREtp::DeleteTimer(const PVR_TIMER &timer, bool bForceDelete)
{
	XBMC->Log(LOG_DEBUG, "PVREtp::DelTimer\n");
	XBMC->Log(LOG_DEBUG, "\tiClientIndex: %d\n", timer.iClientIndex);
	XBMC->Log(LOG_DEBUG, "\tiClientChannelUid: %d\n", timer.iClientChannelUid);
//	XBMC->Log(LOG_DEBUG, "\tstartTime: %" PRId64" (%s)\n", timer.startTime, xmltv_epoch_to_str(timer.startTime));
//	XBMC->Log(LOG_DEBUG, "\tendTime: %" PRId64" (%s)\n", timer.endTime, xmltv_epoch_to_str(timer.startTime));
/*
	XBMC->Log(LOG_DEBUG, "\tstate: %d\n", timer.state);
	XBMC->Log(LOG_DEBUG, "\ttitle: %s\n", timer.strTitle);
	XBMC->Log(LOG_DEBUG, "\tstrDirectory: %s\n", timer.strDirectory);
	XBMC->Log(LOG_DEBUG, "\tstrSummary: %s\n", timer.strSummary);
	XBMC->Log(LOG_DEBUG, "\tiPriority: %d\n", timer.iPriority);
	XBMC->Log(LOG_DEBUG, "\tiLifetime: %d\n", timer.iLifetime);
	XBMC->Log(LOG_DEBUG, "\tbIsRepeating: %d\n", timer.bIsRepeating);
	XBMC->Log(LOG_DEBUG, "\tfirstDay: %" PRId64"\n", timer.firstDay);
	XBMC->Log(LOG_DEBUG, "\tiWeekdays: %d\n", timer.iWeekdays);
	XBMC->Log(LOG_DEBUG, "\tiEpgUid: %d\n", timer.iEpgUid);
	XBMC->Log(LOG_DEBUG, "\tiMarginStart: %d\n", timer.iMarginStart);
	XBMC->Log(LOG_DEBUG, "\tiMarginEnd: %d\n", timer.iMarginEnd);
	XBMC->Log(LOG_DEBUG, "\tiGenreType: %d\n", timer.iGenreType);
	XBMC->Log(LOG_DEBUG, "\tiGenreSubType: %d\n", timer.iGenreSubType);
*/
	if (m_ETPProto)
	{
		if (m_ETPProto->DeleteTimer((uint32_t)timer.iClientIndex) == true)
		{
//			XBMC->Log(LOG_DEBUG, "PVREtp::DeleteTimer GOOD\n");
//			PVR->TriggerTimerUpdate();
			m_ForceUpdateInfo = true;
			return PVR_ERROR_NO_ERROR;
		}
		else
		{
			if (bForceDelete)
				XBMC->Log(LOG_DEBUG, "FORCE DELETE\n");
//			XBMC->Log(LOG_DEBUG, "PVREtp::DelteTimer BAD\n");
			return PVR_ERROR_FAILED;
		}
	}
	return PVR_ERROR_SERVER_TIMEOUT;
}

PVR_ERROR PVREtp::UpdateTimer(const PVR_TIMER &timer)
{
	XBMC->Log(LOG_DEBUG, "PVREtp::UpdateTimer\n");
	XBMC->Log(LOG_DEBUG, "\tiClientIndex: %d\n", timer.iClientIndex);
	XBMC->Log(LOG_DEBUG, "\tiClientChannelUid: %d\n", timer.iClientChannelUid);
	return PVR_ERROR_NOT_IMPLEMENTED;
}


PVR_ERROR PVREtp::SignalStatus(PVR_SIGNAL_STATUS &signalStatus)
{
//								XBMC->Log(LOG_DEBUG, "HAZ SIGNAL NULL PID: %d %d\n", m_SignalStrength, m_SignalNoiseRatio);
	memset(&signalStatus, 0, sizeof(signalStatus));

//	signalStatus.iSignal = (m_SignalStrength * 0xffff) / 100;
//	signalStatus.iSNR = (m_SignalNoiseRatio * 0xffff) / 100;
	signalStatus.iSignal = m_SignalStrength;
	signalStatus.iSNR = m_SignalNoiseRatio;

//	XBMC->Log(LOG_DEBUG, "PVREtp::SignalStatus: %d %d\n", signalStatus.iSignal, signalStatus.iSNR);
//	snprintf(signalStatus.strAdapterName, PVR_ADDON_NAME_STRING_LENGTH - 1, "%s", "TEST Adapter");
//	snprintf(signalStatus.strAdapterStatus, PVR_ADDON_NAME_STRING_LENGTH - 1, "%s", "OK");
	return PVR_ERROR_NO_ERROR;
}

uint32_t PVREtp::PeekAC3FrameSize(uint8_t *data,  uint32_t len)
{
	if (len >= 5 && data[0] == AC3_START_CODE[0] && data[1] == AC3_START_CODE[1])
	{
		uint8_t fscod, frmsizcod;
		fscod = (data[4] & 0xC0) >> 6;
		frmsizcod = (data[4] & 0x3F);
		return (AC3_FRAME_SIZE[frmsizcod][fscod] * 2);
	}
	return 0;
}

uint64_t PVREtp::GetStartTime(void)
{
	if (m_StartTime)
	{
		return m_StartTime;
	}

	if (StreamAlive())
	{
		int count = 5;
		uint64_t tmp1 = ETP_INVALID_TIME;

		while (!m_StartTime)
		{
			XBMC->Log(LOG_DEBUG, "[GetStartTime] Getting Start time..\n");
			tmp1 = m_ETPProto->StreamGetTimeStart();
			if  (tmp1 != ETP_INVALID_TIME)
			{
				if (tmp1 > 45000)
					m_StartTime = (tmp1 - 45000);
				else
					m_StartTime = tmp1;

				m_EndTime = m_ETPProto->StreamGetTimeEnd();
				m_BufTimeOffset = m_EndTime - m_StartTime;
				XBMC->Log(LOG_DEBUG, "[GetStartTime] Setting PTS Starttime to: %" PRId64" (%0.2f), diff: %" PRId64" (%0.2f)\n", m_StartTime, m_StartTime/90000.0, m_BufTimeOffset, m_BufTimeOffset/90000.0 );
				break;
			}
			else
			{
				usleep(1000*1000);
				count--;
				if (count == 0)
				{
					m_StartTime = ETP_INVALID_TIME;
					break;
				}
			}

		}
	}
	return m_StartTime;
}

time_t PVREtp::GetPlayingTime(void)
{
	if (StreamAlive())
	{
		time_t ret = ((m_CurrentTime-m_StartTime)/90000) + (m_StreamOpenTime - m_BufTimeOffset);
//		XBMC->Log(LOG_DEBUG, "------------------- [GetPlayingTime] %" PRId64"\n", ret);
		return ret;
		//return (m_CurrentTime/90000);
	}
	return 0;
}

time_t PVREtp::GetBufferTimeStart(void)
{
	if (StreamAlive())
	{
//	XBMC->Log(LOG_DEBUG, "---------------- [GetPlayingTimeStart] m_StartTime: 0\n");
		time_t ret =  m_StreamOpenTime - m_BufTimeOffset;
		return ret;
	}
	return m_StreamOpenTime - (m_BufTimeOffset/90000);
}

time_t PVREtp::GetBufferTimeEnd(void)
{
//	XBMC->Log(LOG_DEBUG, "------------- [GetPlayingTimeEnd] %" PRId64"\n", m_EndTime/90000);
//	XBMC->Log(LOG_DEBUG, "---- PVREtp::GetPlayingTimeEnd\n");
	if (StreamAlive())
	{
		time_t ret = ((m_EndTime-m_StartTime)/90000) + (m_StreamOpenTime - m_BufTimeOffset);
		return ret;
	}
	return 0;
}

